//
//  HPSwiperFactory.m
//  HandPaySwiperModule
//
//  Created by marco on 14/12/4.
//  Copyright (c) 2014年 marco. All rights reserved.
//

#import "HPSwiperContext.h"
#import "HPSwiperService.h"
#import "HPBaseSwiper.h"
#import "SwiperDefine.h"

#import "ZZTBalanceEnquiry.h"
#import "ZZTCollectMoney.h"
#import "ZZTFastTransfer.h"


const NSString* kSwiperSupportConfigSwipeCode = @"code";


@interface HPSwiperContext()
{
    NSMutableArray* weakRefDelegates;
}
@end

@implementation HPSwiperContext
{
    NSDictionary* swiperNameMapping;
}

+(instancetype)sharedInstance
{
    static HPSwiperContext* simpleFactory = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        simpleFactory = [[self alloc] init];
    });
    return simpleFactory;
}

-(instancetype)init
{
    self = [super init];
    weakRefDelegates = [NSMutableArray array];
    swiperNameMapping = @{@"BBPOS":@"HPBBPOSSwiper",
                          @"BBPOS_IC":@"HPZhijiaSwiper",
                          @"ME11":@"ME11Swiper"};
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(swipeChangeStateOnServiceHandler:) name:SwiperStateChangeNotificationKey object:nil];
    return self;
}

-(void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }

-(void)addDelegate:(__weak id<HPSwiperContextDelegate>)delegate
{
    if (delegate)
    {
        [self removeDelegate:delegate]; // make sure delegate can not be duplicate.
        [weakRefDelegates addObject:[NSValue valueWithNonretainedObject:delegate]];
    }
}

-(void)removeDelegate:(__weak id<HPSwiperContextDelegate>)delegate
{
    if (!delegate)
        return;

    __weak id<HPSwiperContextDelegate> delegateItem = nil;
    for (NSValue* delegateWrapper in weakRefDelegates)
    {
        [delegateWrapper getValue:&delegateItem];
        if (delegateItem == delegate || (!delegateWrapper))
            [weakRefDelegates removeObject:delegateWrapper];
    }
}

-(void)makeDelegatesPerformSelector:(SEL)select argument:(NSObject*)arg
{
    __unsafe_unretained NSObject* delegateItem = nil;
    for (NSValue* delegateWrapper in weakRefDelegates)
    {
        [delegateWrapper getValue:&delegateItem];
        if (!delegateItem)
        {
            [weakRefDelegates removeObject:delegateWrapper];
            continue;
        }
        
        IMP imp = [delegateItem methodForSelector:select];
        if (arg)
        {
            void (*func)(id, SEL, NSObject*) = (void*)imp;
            func(delegateItem, select, arg);
        }
        else
        {
            void (* func)(id, SEL) = (void*)imp;
            func(delegateItem, select);
        }
    }
}

//-(void)notifyAllDelegateWithChoiseYourDevice
//{
//    __weak id<HPSwiperContextDelegate> delegateItem = nil;
//    for (NSValue* delegateWrapper in weakRefDelegates)
//    {
//        [delegateWrapper getValue:&delegateItem];
//        if (delegateItem)
//            [delegateItem choiseYourDevice:self.supportDeviceDescriptions];
//        else // the target is be released.
//            [weakRefDelegates removeObject:delegateWrapper];
//    }
//}
//
//-(void)notifyAllDelegateWithSwiperCsnDidChange:(NSString*)newCsn
//{
//    __unsafe_unretained id<HPSwiperContextDelegate> delegateItem = nil;
//    for (NSValue* delegateWrapper in weakRefDelegates)
//    {
//        [delegateWrapper getValue:&delegateItem];
//        if (delegateItem)
//            [delegateItem swiperCsnDidChange:newCsn];
//        else // the target is be released.
//            [weakRefDelegates removeObject:delegateWrapper];
//    }
//}

-(void)swipeChangeStateOnServiceHandler:(NSNotification*)ntf
{   // do kvo nitify
    if (self.connectedDeviceCSN != self.avaliableService.csn)
    {
        self.connectedDeviceCSN = self.avaliableService.csn;
//        [self notifyAllDelegateWithSwiperCsnDidChange:self.avaliableService.csn];// TODO: current is on main thread? why crash ?
    }
}

-(void)lookupDeviceWithChannelName:(NSString*)cname;
{
    @synchronized(self)
    {
        if (self.avaliableService)
            return;
        // do notify by kvo.
        NSArray* deviceList = [self loadSwiperConfigPlistName:nil bundle:nil];
        self.supportDeviceDescriptions = [self getDevicesFromPlistContent:deviceList channelName:cname];
        
        if (self.supportDeviceDescriptions.count > 1 )
            [self makeDelegatesPerformSelector:NSSelectorFromString(@"choiseYourDevice:")
                                      argument:self.supportDeviceDescriptions];
        else if(self.supportDeviceDescriptions.count == 1)
            [self setSelectedSwiperDescription:self.supportDeviceDescriptions.firstObject];
        else
            @throw [NSException exceptionWithName:nil reason:nil userInfo:nil];
    }
}

-(void)setAvaliableService:(id<HPSwipeServiceProtocal>)avaliableService { _avaliableService = avaliableService; }
-(void)setSupportDeviceDescriptions:(NSArray *)arys{ _supportDeviceDescriptions = arys; }
-(void)setConnectedDeviceCSN:(NSString *)connectedDeviceCSN { _connectedDeviceCSN = connectedDeviceCSN; }

-(id<HPSwipeServiceProtocal>)serviceWithAccountBindInfo:(SwipeBindingInfo*)info
{
    return nil;
}

-(POSProcess*)processorWithBindingInfo:(SwipeBindingInfo*)info businessType:(HPPOSProcessType)type
{
    if (!self.avaliableService)
        return nil;
    
    POSProcess* newInstance = nil;
    if (type == HPPOSBusinessTypeBalance)
        newInstance = [[ZZTBalanceEnquiry alloc] init];
    else if(type == HPPOSBusinessTypeCollectMonety)
        newInstance = [[ZZTCollectMoney alloc] init];
    else
        newInstance = [[ZZTFastTransfer alloc] init];
    newInstance.swipeService = self.avaliableService;
    return newInstance;
}

-(BOOL)setSelectedSwiperDescription:(NSDictionary*)swiperDescription
{
    HPSwiperService* service = self.avaliableService;
    if (service.currentSwiper.hasOperation)
        return NO;
    
    if (!service)
    {
        service = [[HPSwiperService alloc] init];
        self.avaliableService = service; // notify by kvo
    }

    NSString* oldName = swipeNameFromDef(self.selectedSwiperDescription);
    NSString* newName = swipeNameFromDef(swiperDescription);
    BOOL isTheSameSwiper = [oldName isEqualToString:newName];
    
    if ( !isTheSameSwiper || !service.currentSwiper) // may be has ben relase
    {
        _connectedDeviceCSN = nil;
        _selectedSwiperDescription = swiperDescription;
        service.currentSwiper = [self loadSwiperByDeviceDescription:swiperDescription];
        [self loadCSN];
    }
    else if(!self.connectedDeviceCSN)
        [self loadCSN];
    
    return YES;
}

-(void)loadCSN
{
    __weak HPSwiperContext* weakSelf = self;
    [self.avaliableService checkAndConnectSwiper:^(HPSwiperResult* result) {
        weakSelf.connectedDeviceCSN = weakSelf.avaliableService.csn;
        NSString* arg = weakSelf.connectedDeviceCSN ? weakSelf.connectedDeviceCSN : @"";
        [weakSelf makeDelegatesPerformSelector:NSSelectorFromString(@"swiperCsnDidChange:")
                                      argument:arg];
    }];
}

-(void)clearContextAndFoceStopProcess
{
    self.connectedDeviceCSN = nil;
    self.selectedSwiperDescription = nil;
    HPSwiperService* service = self.avaliableService;
    service.currentSwiper = nil;
    self.avaliableService = nil;
}
@end


@implementation HPSwiperContext(HPSwipeServiceFactory)

-(NSArray*)loadSwiperConfigPlistName:(NSString*)fileName bundle:(NSBundle*)bundle
{
    if (!fileName.length)
        fileName = @"SwiperType";
    if (!bundle)
        bundle = [NSBundle mainBundle];
    
    NSString* swiperPath = [[NSBundle mainBundle] pathForResource:@"SwiperType" ofType:@"plist"];
    NSArray* rootItems = [NSArray arrayWithContentsOfFile:swiperPath];
    return rootItems;
}

-(NSArray*)getDevicesFromPlistContent:(NSArray*)swiperArry channelName:(NSString*)cname
{
    for (NSDictionary *dict in swiperArry)
    {
        if ([(NSString *)[dict objectForKey:@"channel"] isEqualToString:cname])
        {
            NSArray* swipers = [dict objectForKey:@"swiper"];
            return swipers;
        }
    }
    return nil;
}

-(HPBaseSwiper*)loadSwiperByDeviceDescription:(NSDictionary*)swiperDescription
{
    NSString* swiperName = [swiperDescription objectForKey:@"code"];
    NSString* clzName = [swiperNameMapping objectForKey:swiperName];
    HPBaseSwiper* swiper = [[NSClassFromString(clzName) alloc] init];
    return swiper;
}
@end